package tools

import (
	"context"
	"encoding/json"
	"os"
	"time"

	grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
	"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
	"github.com/natefinch/lumberjack"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

/*
 +----------------------------------------------------------------------
 + Title        : zap 日志
 + Author       : huwl
 + Version      : 1.0.0
 + Initial-Time : 2021-09-22
 + Last-time    : 这个文件最后修改的时间 + 修改人的名称
 + Desc         : zap 日志 https://github.com/uber-go/zap
 +----------------------------------------------------------------------
*/

// 定义上下文键类型
type contextKey string

const (
	requestBodyKey contextKey = "request_body"
)

// 拦截器：捕获请求体并存入上下文
func RequestCaptureInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
	// 将请求体存入上下文
	ctx = context.WithValue(ctx, requestBodyKey, req)
	return handler(ctx, req)
}

// 初始化带滚动功能的 Zap 日志记录器
func InitLogger() *zap.Logger {
	// 配置编码器
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder

	// 创建控制台编码器（开发环境）
	consoleEncoder := zapcore.NewConsoleEncoder(encoderConfig)

	// 创建 JSON 编码器（生产环境）
	jsonEncoder := zapcore.NewJSONEncoder(encoderConfig)

	// 配置日志滚动
	fileWriter := zapcore.AddSync(&lumberjack.Logger{
		Filename:   "logs/grpc.log",
		MaxSize:    ZapLoggerMaxSize, // 默认100MB
		MaxBackups: ZapLoggerMaxBackups,
		MaxAge:     ZapLoggerMaxAge, // days
		Compress:   ZapLoggerCompress,
	})

	// 设置日志级别
	atomicLevel := zap.NewAtomicLevel()
	atomicLevel.SetLevel(zap.DebugLevel) // 生产环境可设为 InfoLevel

	// 创建核心
	core := zapcore.NewTee(
		// 控制台输出（开发环境）
		zapcore.NewCore(consoleEncoder, zapcore.AddSync(os.Stdout), atomicLevel),

		// 文件输出（生产环境）
		zapcore.NewCore(jsonEncoder, fileWriter, atomicLevel),
	)

	// 创建记录器
	logger := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel))

	// 添加全局字段
	return logger.With(
		zap.String("service", ServiceName),
		zap.String("version", ServiceVersion),
	)
}

// 自定义日志消息生成器（添加额外字段）
func CustomMessageProducer(ctx context.Context, msg string, level zapcore.Level, code codes.Code, err error, duration zapcore.Field) {
	// 从上下文中提取请求ID
	requestID := "unknown"
	if id, ok := ctx.Value("request_id").(string); ok {
		requestID = id
	}

	// 从上下文获取请求参数
	var requestBody interface{}
	if body, ok := ctx.Value(requestBodyKey).(interface{}); ok {
		// 请求数据转换成json格式
		bodyMap := map[string]interface{}{}
		b, _ := json.Marshal(body)
		json.Unmarshal(b, &bodyMap)
		requestBody = bodyMap
	}

	// 构建日志字段
	fields := []zapcore.Field{
		zap.Any("grpc.request", requestBody), // 添加请求参数
		duration,                             // 将传入的duration字段加入
		zap.String("request.id", requestID),
	}

	if err != nil {
		fields = append(fields, zap.Error(err))
	}

	// 关键：创建一个新的duration字段来满足DefaultMessageProducer的要求
	dummyDuration := zap.Duration("grpc.duration", time.Duration(duration.Integer))

	// 使用上下文中的logger（它已经包含grpc.method等字段）
	l := ctxzap.Extract(ctx)

	// 6. 创建新的上下文，携带自定义字段
	newCtx := ctxzap.ToContext(ctx, l.With(fields...))

	// 记录日志
	grpc_zap.DefaultMessageProducer(newCtx, msg, level, code, err, dummyDuration)
}

// 自定义错误恢复处理器
func RecoveryHandler(ctx context.Context, p interface{}) error {
	logger := grpc_zap.Extract(ctx)

	// 记录详细的错误信息
	logger.Error("gRPC 服务发生 panic",
		zap.Any("panic", p),
		zap.Stack("stack"),
	)

	// 返回标准错误响应
	return status.Errorf(codes.Internal, "内部服务器错误")
}
